/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit.command;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getCovereds;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.Lifeline;

import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.figures.Grid;
import com.cea.papyrus.diagram.sequence.sedi.figures.LifelineFigure;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Determines the preferred bounds for an ExecutionOccurrence fragment.
 * <p>
 * The following attributes must be set before the command can run successfully:
 * </p>
 * <ul>
 * <li>AnyEditPart
 * <li>ExecutionOccurrence or Lifeline
 * <li>Hint - in absolute coordinates
 * </ul>
 * <p>
 * When completed, the following attributes can be read to access the result:
 * </p>
 * <ul>
 * <li>PreferredBounds
 * </ul>
 * 
 * @author frankd@ifi.uio.no
 */
public class DecideExecutionOccurrenceBoundsCommand extends Command {

	private static final int WIDTH = 1;
	private static final int DEFAULT_HEIGHT = 2 * Grid.ANCHOR_DELTA;
	private static final int MIN_HEIGHT = Grid.ANCHOR_DELTA;

	private ExecutionSpecification executionOccurrence;
	private EditPart anyEditPart;
	private Lifeline coveredLifeline;
	private Rectangle hint;
	private Rectangle preferred;

	public void setExecutionSpecification(final ExecutionSpecification eo) {
		this.executionOccurrence = eo;
	}

	public void setAnyEditPart(final EditPart editPart) {
		this.anyEditPart = editPart;
	}

	public void setLifeline(final Lifeline lifeline) {
		this.coveredLifeline = lifeline;
	}

	public void setHint(final Rectangle hint) {
		this.hint = hint.getCopy();
	}

	public Rectangle getPreferredBounds() {
		return preferred;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void execute() {
		if (coveredLifeline == null
				&& (executionOccurrence.getStart() == null || executionOccurrence
						.getStart().getCovereds().isEmpty())) {
			final int prefHeight = (hint.height / Grid.ANCHOR_DELTA + 1)
					* Grid.ANCHOR_DELTA;
			preferred = new Rectangle(hint.x, hint.y, WIDTH, prefHeight);
			return;
		}

		final Lifeline lifeline = (coveredLifeline != null) ? coveredLifeline
				: getCovereds(executionOccurrence.getStart()).get(0);

		GraphElement viewElement = (GraphElement) anyEditPart.getModel();
		List<GraphNode> listLifeline = LookForElement
				.lookForGraphElementWODiagram(lifeline, LookForElement
						.getDiagram(viewElement), new ArrayList<GraphNode>(1));
		GraphNode lifelineView = listLifeline.get(0);
		Rectangle lifelineBounds = new Rectangle(lifelineView.getPosition(),
				lifelineView.getSize());
		final int lifelineX = lifelineBounds.x + lifelineBounds.width / 2;
		List<GraphNode> listEO = LookForElement.lookForGraphElementWODiagram(
				executionOccurrence, LookForElement.getDiagram(viewElement),
				new ArrayList<GraphNode>(1));
		GraphNode eoView = listEO.get(0);
		Rectangle eoBounds = new Rectangle(eoView.getPosition(), eoView
				.getSize());
		GraphicalEditPart eoEditPart = (GraphicalEditPart) anyEditPart
				.getViewer().getEditPartRegistry().get(eoView);

		// We want EOfigures to snap to the grid lines vertically,
		// as well as being an integral number of gridlines tall -
		// unless it is near the head of the lifeline, in which case
		// it should snap upwards to the head
		final boolean snaphead = (hint.y < lifelineBounds.y
				+ LifelineFigure.HEAD_HEIGHT + Grid.ANCHOR_DELTA);

		int deltay = Grid.findNextGridLine(eoEditPart.getFigure(), hint.y
				- eoBounds.y);
		int starty = eoBounds.y + deltay;
		int height = hint.height == 0 ? DEFAULT_HEIGHT : Math.max(hint.height,
				MIN_HEIGHT);
		height = (height / Grid.ANCHOR_DELTA) * Grid.ANCHOR_DELTA + 1;
		if (snaphead) {
			final int snapdelta = starty
					- (lifelineBounds.y + LifelineFigure.HEAD_HEIGHT);
			starty -= snapdelta;
			if (snapdelta > 0)
				height += snapdelta;
		}

		preferred = new Rectangle(lifelineX, starty, 1, height);
	}

	@Override
	public void redo() {
		execute();
	}

	public void dispose() {
		executionOccurrence = null;
		coveredLifeline = null;
		anyEditPart = null;
		preferred = null;
	}
}
